package boardGenerator;
import java.util.ArrayList;
import java.util.Random;

/**
 * Generador de tableros Sudokus Valido Version 1
 * Genera tableros utilizando fueza bruta y backtraking
 */
public class SudokuGenerator1 {
    /*Tamaños y Repeticiones*/
    private final static int MAX_REPEATITIONS = 1000;
    private final static int width = 9;
    private final static int height = 9;
    /*Tablero*/
    private int[][] board = new int[width][height];

    /**
     * Genera la solucion
     * @return 
     */
    public boolean generateSolutionGrid() {
        Random rand = new Random();
        ArrayList<Integer> freeCols = new ArrayList<Integer>(9);
        ArrayList<Utils.Point> filledPoints = new ArrayList<Utils.Point>(9);
        for (int i=1;i<10;i++){
            int repeatitions = 0;
            filledPoints.clear();
            int col = -1;
            for (int row=0;row<9;row+=2) {
                repeatitions++;
                if (repeatitions>MAX_REPEATITIONS) {
                    return false;
                }
                freeCols.clear();
                for (int j=0;j<9;j++) {
                    freeCols.add(j);
                }
                boolean startOver=true;
                while (startOver) {
                    startOver=false;
                    int size=freeCols.size();
                    if (size==0) {
                        for (int rollback=0;rollback< filledPoints.size();rollback++) {
                            Utils.Point p = (Utils.Point) filledPoints.get(rollback);
                            this.board[p.x][p.y]=0;

                        }
                        row = -2;
                        startOver = true;
                        break;
                    }
                    // pick a column from to insert i
                    int randNum = rand.nextInt(size);
                    col = freeCols.get(randNum);

                    if (this.board[row][col] != 0) {
                        freeCols.remove(randNum);
                        startOver = true;
                        continue;
                    }

                    int HB = (int) (row / 3);
                    int VB = (int) (col / 3);

                    for (int k = 0; k < 9; k++) {
                        if (board[k][col] == i) {
                            freeCols.remove(randNum);
                            startOver = true;
                        }
                    }

                    if (startOver)
                        continue;

                    for (int k = HB * 3; k < (HB + 1) * 3; k++) {
                        for (int j = VB * 3; j < (VB + 1) * 3; j++) {
                            if (board[k][j] == i) {
                                freeCols.remove(randNum);
                                startOver = true;
                            }
                        }
                    }
                }

                if (startOver) {
                    continue;
                }

                // insert i into the valid column
                this.board[row][col] = i;
                filledPoints.add(new Utils.Point(row, col));

                if (row == 8) {
                    row = -1;
                }
            } // end for row

        } // end for i

        return true;

    }

    /**
     * Genera el Tablero Problema
     * @param difficultyLevel
     * @return 
     */
    public boolean generateProblemGrid(GameDifficultyLevel difficultyLevel) {
        int noTries = MAX_REPEATITIONS;
        while ( noTries > 0 && ! generateSolutionGrid() ) {
            clearBoard();
            noTries--;
        }
        if ( noTries != 0 ) {
            int numUnfilledBoxes = difficultyLevel.getDifficulty(); // base it on the difficulty level
            Random rand = new Random();
            ArrayList<Pair<Integer,Integer>> test = new ArrayList<Pair<Integer,Integer>>();
            while ( numUnfilledBoxes > 0 ) {
                int xBox = rand.nextInt(9);
                int yBox = rand.nextInt(9);
                Pair<Integer,Integer> tmp = new Pair<Integer,Integer>(xBox,yBox);
                
                if(!(test.contains(tmp))){
                int savedVal1 = board[xBox][yBox];
                    board[xBox][yBox] = 0;
                    if (!Solutions.hasUniqueSolution(board)) {
                        board[xBox][yBox] = savedVal1;
                    } else {
                        numUnfilledBoxes--;
                        test.add(tmp);
                    }
                }
            }            
            return true;
        } 
        return false;
    }
    
    /**
     * Limpia el Tablero
     */
    private void clearBoard() {
        for(int i=0; i < height; i++) {
            for(int j=0; j < width; j++) {
                board[i][j] = 0;
            }
        }
    }
    
    /**
     * Obtiene el tablero Generado
     * @return 
     */
    public int[][] getBoard() {
        return board;
    }
    
    /**
     * Obtiene la solucion al tablero generado
     * @return 
     */
    public int[][] getSolution() {
        return Solutions.getSolutions().get(0);
    }
    
    /**
     * Obtiene la cantidad de numeros que muestra el problema
     * @return 
     */
    public int getNumberCount(){
        return Utils.numberDisplay(this.board);
    }
}
